/*
 * @(#)GenSrc.java	1.7 06/10/10
 *
 * Copyright  1990-2008 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation. 
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt). 
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA 
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions. 
 */

package sun.tools.javazic;

import	java.io.IOException;
import	java.io.File;
import	java.io.FileWriter;
import	java.io.BufferedWriter;
import	java.util.ArrayList;
import	java.util.Iterator;
import	java.util.LinkedList;
import	java.util.Set;
import	java.util.TreeMap;
import	java.util.TreeSet;
import	sun.util.calendar.Gregorian;
import	sun.util.calendar.ZoneInfoFile;

/**
 * <code>GenSrc</code> is one of back-end classes of javazic, and generates 
 * ZoneInfoMappings.java and zone-specific .java file for each zone.
 */
class GenSrc extends BackEnd {

    private static final String docDir = "text";

    private static final String m_PackageName = 
	"package sun.util.calendar.zoneinfo;\n\n";
    private static final String m_ImportClasses = 
	"import java.util.TimeZone;\n" +
	"import java.util.Calendar;\n";
    private static final String m_ImportSTZClass = 
	"import java.util.SimpleTimeZone;\n";
    private static final String m_additionalImportClass = 
	"import sun.util.calendar.ZoneInfo;\n\n";
    private static final String m_header = 
    "/*\n"+
    " * Copyright  1990-2008 Sun Microsystems, Inc. All Rights Reserved.\n" +
    " * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER\n" +
    " * \n" +
    " * This program is free software; you can redistribute it and/or\n" +
    " * modify it under the terms of the GNU General Public License version\n" +
    " * 2 only, as published by the Free Software Foundation. \n" +
    " * \n" +
    " * This program is distributed in the hope that it will be useful, but\n" +
    " * WITHOUT ANY WARRANTY; without even the implied warranty of\n" +
    " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" +
    " * General Public License version 2 for more details (a copy is\n" +
    " * included at /legal/license.txt).\n" +
    " * \n" +
    " * You should have received a copy of the GNU General Public License\n" +
    " * version 2 along with this work; if not, write to the Free Software\n" +
    " * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA\n" +
    " * 02110-1301 USA \n" +
    " * \n" +
    " * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa\n" +
    " * Clara, CA 95054 or visit www.sun.com if you need additional\n" +
    " * information or have any questions.\n" +
	" */\n\n";
    private static final String m_warning =
	" // THIS FILE HAS BEEN GENERATED BY A TOOL. DO NOT EDIT THIS FILE MANUALLY.\n";
    private static final String m_rule_zone_Begin = 
	"/**\n"+
	" * ZoneInfo definitions for \"";
    private static final String m_rule_zone_Mid = 
	".\" The following have been derived\n"+
	" * from the Olson public zone information.\n"+
	" * <pre><code>\n";
    private static final String m_rule = 
	" * Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S\n";
    private static final String m_zone = 
	" * Zone                 GMTOFF  RULES   FORMAT  [UNTIL]\n";
    private static final String m_rule_zone_End = 
	" * </code></pre>\n"+
	" *\n"+
	" */\n\n";
    private static final String m_return = "\n";
    private static final String m_comment_null_line = " //\n";
    private static final String m_comment_blank_line = " * \n";
    private static final String m_array_End = "    };\n\n";
    private static final String m_method_End = "    }\n\n";
    private static final String m_class_End = "}\n";

    /**
     * Generates datafile in .java format for each zone.
     * @param Timezone
     * @return 0 if no errors, or 1 if error occurred.
     */
    int processZoneinfo(Timezone tz) {
	try {
	    int size;
	    String outputDir = Main.getOutputDir();
	    String zonename = tz.getName();
	    String zonefile = ZoneInfoFile.getFileName(zonename) + ".java";

	    /* If outputDir doesn't end with file-separator, adds it. */
	    if (!outputDir.endsWith(File.separator)) {
		outputDir += File.separatorChar;	
	    }
	    outputDir += docDir + File.separatorChar;

	    /* If zonefile includes file-separator, it's treated as part of
	     * pathname. And make directory if necessary.
	     */
	    int index = zonefile.lastIndexOf(File.separatorChar);
	    if (index != -1) {
		outputDir += zonefile.substring(0, index+1);
	    }
	    File outD = new File(outputDir);
	    outD.mkdirs();

	    /* Open zoneinfo file to write. */
	    FileWriter fw = new FileWriter(outputDir + zonefile.substring(index+1));
	    BufferedWriter out = new BufferedWriter(fw);

	    /* Output header. */
	    out.write(m_header);
	    out.write(m_comment_null_line + m_warning + m_comment_null_line + m_return);
	    out.write(m_PackageName + m_ImportClasses);

	    /* whether SimpleTimeZone is used (ArrayList) or not (null) */
	    ArrayList stz = tz.getLastRules();
	    if (stz != null) {
    		out.write(m_ImportSTZClass);
	    }
    	    out.write(m_additionalImportClass);

	    /* Output Rule and Zone. */
	    out.write(m_rule_zone_Begin + zonename + m_rule_zone_Mid);

	    /* Output Rule records. */
	    ArrayList rule = tz.getRules();
	    if (rule != null) {
		size = rule.size();
		out.write(m_rule);
		for (int i = 0; i < size; i++) {
		    out.write(" * " + ((RuleRec)rule.get(i)).getLine() + 
			      m_return);
		}
		out.write(m_comment_blank_line);
	    }

	    /* Output Zone records. */
	    ArrayList zone = tz.getZones();
	    if (zone != null) {
		size = zone.size();
		out.write(m_zone);
		for (int i = 0; i < size; i++) {
		    out.write(" * " + ((ZoneRec)zone.get(i)).getLine() +
			      m_return);
		}
	    }
	    out.write(m_rule_zone_End);

	    /* beginning of zonename class */
	    out.write("public class " + zonefile + " extends ZoneInfo {\n");

	    /* Output Transaction records. */
	    ArrayList transitions = tz.getTransitions();
	    if (transitions != null) {
		ArrayList dstOffsets = tz.getDstOffsets();
		ArrayList offsets = tz.getOffsets();

		if ((dstOffsets == null && offsets != null) ||
		    (dstOffsets != null && offsets == null)) {
		    Main.panic("Data not exist. (dstOffsets or offsets)");
		    return 1;
		}

		out.write("    private static final long[] pTransitions = {\n\t");
		size = transitions.size();
		long tra = 0;
		for (int i = 0; i < size; i++) {
		    int	dstoffset;

		    /* if year is different from previous one, insert "\n". */
		    tra = ((Long)transitions.get(i)).longValue();

		    /* if DST offset 0, no DST (not index 0). */
		    if ((dstoffset = ((Integer)dstOffsets.get(i)).intValue())
			== -1) {
			dstoffset = 0;
		    }

		    out.write(tra + "L<<12|" + dstoffset + "<<4|" +
			      ((Integer)offsets.get(i)).intValue() + ", " +
			      " // " + Gregorian.getCalendarDate(tra) + "\n\t");
		}
		out.write(m_return + m_array_End);

		ArrayList gmtoffset = tz.getGmtOffsets();
		if (gmtoffset != null) {
		    out.write("    private static final int[] pOffsets = {\n\t");
		    int gmtoffset_size = gmtoffset.size();
		    for (int i = 0; i < gmtoffset_size; i++) {
			out.write(((Integer)gmtoffset.get(i)).intValue() + ", ");
		    }
		    out.write(m_return + m_array_End);
		}
	    }

	    if (stz != null) { 
		RuleRec rr0 = (RuleRec)stz.get(0);
		RuleRec rr1 = (RuleRec)stz.get(1);
		boolean wall = rr0.getTime().isWall() && rr1.getTime().isWall();

		out.write("    private static final int[] pSimpleTimeZoneParams = {\n" +
			  "\t" + Month.toString(rr0.getMonthNum()) + ", " +
			  rr0.getDay().getDayForSimpleTimeZone() + ", " + 
			  rr0.getDay().getDayOfWeekForSimpleTimeZone() + ", " +
			  Time.toFormedString((int)rr0.getTime().getTime()) + ", " +
			  (wall ? "\n" :
			   (rr0.getTime().getTypeForSimpleTimeZone() + ",\n")) + 
			  "\t" + Month.toString(rr1.getMonthNum()) + ", " +
			  rr1.getDay().getDayForSimpleTimeZone() + ", " + 
			  rr1.getDay().getDayOfWeekForSimpleTimeZone() + ", " +
			  Time.toFormedString((int)rr1.getTime().getTime()) + ", " +
			  (wall ? "" :
			   (rr1.getTime().getTypeForSimpleTimeZone())) +
			  m_return + m_array_End);
	    }

	    /* Output method zonename(). */
	    out.write("    public " + zonefile + "() {\n" + "\tsuper(\"" +
		      zonename + "\", " + 
		      Time.toFormedString(tz.getRawOffset()) + ", " + 
		      Time.toFormedString(tz.getLastDSTSaving()) + ", " +
		      "0x" + Integer.toHexString(tz.getCRC32()) + ", \n\t    ");
	    out.write((transitions != null) ? 
		      "pTransitions, " + "pOffsets, " : "null, " + "null, ");
	    out.write((stz != null) ? "pSimpleTimeZoneParams" : "null");
	    out.write(");\n");

	    out.write(m_method_End);
    	    out.write(m_class_End);	/* End of zonename class */

	    out.close();
	    fw.close();
	} catch(IOException e) {
	    Main.panic("IO error: "+e.getMessage());
	    return 1;
	}

	return 0;
    }

    /**
     * Generates ZoneInfoMappings.java.
     * @param Mappings
     * @return 0 if no errors, or 1 if error occurred.
     */
    int generateSrc(Mappings map) {
	try {
	    int roi_size;
	    int pad_num;
	    Iterator keys;
	    String key;
	    String outputDir = Main.getOutputDir();

	    /* Whether rawOffsetIndex list exists or not. */
	    LinkedList roi = map.getRawOffsetsIndex();
	    if (roi == null) {
		Main.panic("Data not exist. (rawOffsetsIndex)");
		return 1;
	    }
	    roi_size = roi.size();	
	    LinkedList roit = map.getRawOffsetsIndexTable();
	    if (roit == null || roit.size() != roi_size) {
		Main.panic("Data not exist. (rawOffsetsIndexTable) Otherwise, Invalid size");
		return 1;
	    }

	    /* Whether alias list exists or not. */
	    TreeMap a = map.getAliases();
	    if (a == null) {
		Main.panic("Data not exist. (aliases)");
		return 1;
	    }

	    /* If outputDir doesn't end with file-separator, adds it. */
	    if (!outputDir.endsWith(File.separator)) {
		outputDir += File.separatorChar;
	    }
	    outputDir += docDir + File.separatorChar;

	    File outD = new File(outputDir);
	    outD.mkdirs();
	
	    /* Open ZoneInfoMapping file to write. */
	    FileWriter fw = new FileWriter(outputDir + "ZoneInfoMappings.java", false);
	    BufferedWriter out = new BufferedWriter(fw);

	    /* Output header. */
	    out.write(m_header);
	    out.write(m_comment_null_line + m_warning);
	    out.write(" //   BASED ON : " + Main.getVersionName() + "\n" + m_comment_null_line + m_return);
	    out.write(m_PackageName);

	    /* beginning of ZoneInfoMapping class */
	    out.write("public class ZoneInfoMappings {\n\n");

	    /* Output sorted rawOffset list. */
	    out.write("    public static final String[] IDs = {\n");

	    for (int i = 0; i < roi_size; i++) {
		TreeSet perRO = (TreeSet)roit.get(i);
		keys = perRO.iterator();

		while (keys.hasNext()) {
		    key = (String)keys.next();
		    pad_num = key.length();
		    pad_num = (pad_num > 20) ? 1 : 21-pad_num;
		    out.write("\t\"" + key + "\",");
		    while (pad_num-- > 0) {
			out.write(" ");
		    }
		    out.write("\t// " + 
			Time.toGMTFormat(roi.get(i).toString()) + "\n");
		}
	    }
	    out.write(m_array_End);

	    /* Output sorted rawOffset list. */
	    out.write("    public static final int[] rawOffsets = {\n");
	    for (int i = 0; i < roi_size; i++) {
		out.write("\t" + roi.get(i) + ",\t// " + i + "\n");
	    }
	    out.write(m_array_End);

	    /* Output sorted rawOffsetIndex list. */
	    out.write("    public static final byte[] rawOffsetIndices = {");
	    int counter = 0;
	    for (int i = 0; i < roi_size; i++) {
	        int num = ((TreeSet)(roit.get(i))).size();

		for (int j = 0; j < num; j++) {
		    if (counter++ % 10 == 0) {
			out.write("\n\t");
		    }
		    if (i < 10) {
			out.write(" ");
		    }
		    out.write(i + ", ");
	 	}
	    }
	    out.write(m_return + m_array_End);

	    out.write("    public static final String[] aliases = {\n");
	    Set s = a.keySet();
	    keys = s.iterator();
	    while (keys.hasNext()) {
		key = (String)keys.next();
		pad_num = key.length();
		pad_num = (pad_num > 20) ? 1 : 21-pad_num;
		out.write("\t\"" + key + "\",");
		while (pad_num-- > 0) {
		    out.write(" ");
		}
		out.write("\"" + a.get(key) + "\",\n");
	    }
	    out.write(m_array_End + m_class_End);

	    out.close();
	    fw.close();
	} catch(IOException e) {
	    Main.panic("IO error: "+e.getMessage());
	    return 1;
	}

	return 0;
    }
}
